package com.kfm.db;

import com.mysql.cj.jdbc.ConnectionImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class ConnectionPool {
    private String url;

    private String user;

    private String password;

    private List<Connection> connections;

    private int size;

    public ConnectionPool(int size, String url, String user, String password){
        this.connections = new ArrayList<>(size);
        this.size = size;
        this.url = url;
        this.user = user;
        this.password = password;
        for (int i = 0; i < size; i++) {
            /*
                代理模式
             */
            Connection o = createConnection(url, user, password);
            connections.add(o);
        }
    }

    public Connection getConnection(){
        if (connections.size() > 0){
            return connections.remove(connections.size() - 1);
        } else {
            return createConnection(url, user, password);
        }

    }

    private Connection createConnection(String url, String user, String password){
        try {
            // 被代理
            final Connection connection = DriverManager.getConnection(url, user, password);
            // JDK 动态代理
            Connection o = (Connection) Proxy.newProxyInstance(ConnectionPool.class.getClassLoader(),
                    new Class[]{Connection.class},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            String name = method.getName();
                            if ("close".equals(name)){
                                if (connections.size() < size){
                                    System.out.println(proxy + " 放回池中");
                                    connections.add((Connection) proxy);
                                } else {
                                    System.out.println(connection + " 关闭了");
                                    connection.close();
                                }
                            }
                            // 不改变其他操作
                            return method.invoke(connection, args);
                        }
                    }
            );
            return o;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }

    public void release(Connection connection){
        if (connections.size() < size) {
            connections.add(connection);
        } else {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void closeAll(){

        ArrayList<Connection> objects = new ArrayList<>();
        objects.addAll(connections);
        connections = new ArrayList<>(0);

        for (Connection connection : objects){
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private class MyConnectionImpl extends ConnectionImpl {

        private Connection conn;

        public MyConnectionImpl(Connection conn){
            this.conn = conn;
        }

        // 重写很多方法

        @Override
        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return conn.prepareStatement(sql);
        }

        @Override
        public void close() throws SQLException {
            if (connections.size() < size) {
                System.out.println(this + "放回池中");
                connections.add(this);
            } else{
                System.out.println(this + "关闭掉");
                conn.close();
            }
        }
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
